+2006-11-16 Michael Natterer <mitch@imendio.com>
+
+ Add new infrastructure for notifications of failed keyboard
+ navigation and navigation with restricted set of keys.
+
+ The patch handles configurable beeping, navigating the GUI with
+ cursor keys only (as in phone environments), and configurable
+ wrap-around. Fixes bugs #322640, #70986, #318827, #334726, #334742
+ and #309291.
+
+ * gtk/gtksettings.c: added properties gtk-keynav-cursor-only,
+ gtk-keynav-wrap-around and gtk-error-bell.
+
+ * gtk/gtkwidget.[ch]: added new signal "keynav-failed" and public
+ API to emit it. Added New function gtk_widget_error_bell() which
+ looks at the gtk-error-bell setting and calls gdk_window_beep()
+ accordingly.
+
+ * gtk/gtk.symbols: add the new widget symbols.
+
+ * gtk/gtkcellrendereraccel.c
+ * gtk/gtkimcontextsimple.c
+ * gtk/gtkmenu.c
+ * gtk/gtknotebook.c: use gtk_widget_error_bell() or look at the
+ gtk-error-bell setting instead of calling gdk_display_beep()
+ unconditionally.
+
+ * gtk/gtkcombobox.c
+ * gtk/gtkentry.c
+ * gtk/gtkiconview.c
+ * gtk/gtklabel.c
+ * gtk/gtkmenushell.c
+ * gtk/gtkspinbutton.c
+ * gtk/gtktextview.c
+ * gtk/gtktreeview.c: call gtk_widget_error_bell() on failed keynav.
+
+ * gtk/gtkentry.c
+ * gtk/gtklabel.c
+ * gtk/gtkrange.c
+ * gtk/gtktextview.c: consult gtk_widget_keynav_failed() on failed
+ cursor navigation and leave the widget if it returns FALSE.
+
+ * gtk/gtkmenushell.c
+ * gtk/gtknotebook.c: only wrap around if gtk-keynav-wrap-around
+ is TRUE.
+
+ * gtk/gtkradiobutton.c: ask gtk_widget_keynav_failed() to decide
+ whether to to wrap-around, and don't select active items on cursor
+ navigation if gtk-keynav-cursor-only is TRUE. Should look at
+ gtk-keynav-wrap-around too, will look into that.
+
2006-11-16 Emmanuele Bassi <ebassi@gnome.org>
* gtk/gtkrecentmanager.c:
gtk_widget_destroy
gtk_widget_destroyed
gtk_widget_ensure_style
+gtk_widget_error_bell
gtk_widget_event
gtk_widget_freeze_child_notify
gtk_widget_get_accessible
gtk_widget_intersect
gtk_widget_is_ancestor
gtk_widget_is_focus
+gtk_widget_keynav_failed
gtk_widget_list_accel_closures
gtk_widget_list_mnemonic_labels
gtk_widget_map
{
if (!gtk_accelerator_valid (accel_key, accel_mods))
{
- gdk_display_beep (display);
+ gtk_widget_error_bell (widget);
return TRUE;
}
gboolean found;
if (!combo_box->priv->model)
- return;
+ {
+ gtk_widget_error_bell (GTK_WIDGET (combo_box));
+ return;
+ }
active_iter = gtk_combo_box_get_active_iter (combo_box, &iter);
return;
}
- if (found)
+ if (found && active_iter)
{
- if (active_iter)
- {
- GtkTreePath *old_path;
- GtkTreePath *new_path;
+ GtkTreePath *old_path;
+ GtkTreePath *new_path;
- old_path = gtk_tree_model_get_path (combo_box->priv->model, &iter);
- new_path = gtk_tree_model_get_path (combo_box->priv->model, &new_iter);
+ old_path = gtk_tree_model_get_path (combo_box->priv->model, &iter);
+ new_path = gtk_tree_model_get_path (combo_box->priv->model, &new_iter);
- if (gtk_tree_path_compare (old_path, new_path) == 0)
- found = FALSE;
+ if (gtk_tree_path_compare (old_path, new_path) == 0)
+ found = FALSE;
- gtk_tree_path_free (old_path);
- gtk_tree_path_free (new_path);
- }
+ gtk_tree_path_free (old_path);
+ gtk_tree_path_free (new_path);
+ }
- if (found)
- {
- gtk_combo_box_set_active_iter (combo_box, &new_iter);
- return;
- }
+ if (found)
+ {
+ gtk_combo_box_set_active_iter (combo_box, &new_iter);
+ }
+ else
+ {
+ gtk_widget_error_bell (GTK_WIDGET (combo_box));
}
}
return TRUE;
}
- else if (event->button == 2 && event->type == GDK_BUTTON_PRESS && entry->editable)
+ else if (event->button == 2 && event->type == GDK_BUTTON_PRESS)
{
- priv->insert_pos = tmp_pos;
- gtk_entry_paste (entry, GDK_SELECTION_PRIMARY);
-
- return TRUE;
+ if (entry->editable)
+ {
+ priv->insert_pos = tmp_pos;
+ gtk_entry_paste (entry, GDK_SELECTION_PRIMARY);
+ return TRUE;
+ }
+ else
+ {
+ gtk_widget_error_bell (widget);
+ }
}
else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
{
*/
return TRUE;
+ if (!entry->editable && event->length)
+ gtk_widget_error_bell (widget);
+
return FALSE;
}
n_chars = g_utf8_strlen (new_text, new_text_length);
if (entry->text_max_length > 0 && n_chars + entry->text_length > entry->text_max_length)
{
- gdk_display_beep (gtk_widget_get_display (GTK_WIDGET (entry)));
+ gtk_widget_error_bell (GTK_WIDGET (entry));
n_chars = entry->text_max_length - entry->text_length;
new_text_length = g_utf8_offset_to_pointer (new_text, n_chars) - new_text;
}
new_pos = current_x < bound_x ? entry->current_pos : entry->selection_bound;
else
new_pos = current_x > bound_x ? entry->current_pos : entry->selection_bound;
-
break;
}
case GTK_MOVEMENT_LOGICAL_POSITIONS:
break;
case GTK_MOVEMENT_VISUAL_POSITIONS:
new_pos = gtk_entry_move_visually (entry, new_pos, count);
+ if (entry->current_pos == new_pos)
+ {
+ if (!extend_selection)
+ {
+ if (!gtk_widget_keynav_failed (GTK_WIDGET (entry),
+ count > 0 ?
+ GTK_DIR_RIGHT : GTK_DIR_LEFT))
+ {
+ GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (entry));
+
+ if (toplevel)
+ gtk_widget_child_focus (toplevel,
+ count > 0 ?
+ GTK_DIR_RIGHT : GTK_DIR_LEFT);
+ }
+ }
+ else
+ {
+ gtk_widget_error_bell (GTK_WIDGET (entry));
+ }
+ }
break;
case GTK_MOVEMENT_WORDS:
while (count > 0)
new_pos = gtk_entry_move_backward_word (entry, new_pos, FALSE);
count++;
}
+ if (entry->current_pos == new_pos)
+ gtk_widget_error_bell (GTK_WIDGET (entry));
break;
case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
case GTK_MOVEMENT_PARAGRAPH_ENDS:
case GTK_MOVEMENT_BUFFER_ENDS:
new_pos = count < 0 ? 0 : entry->text_length;
+ if (entry->current_pos == new_pos)
+ gtk_widget_error_bell (GTK_WIDGET (entry));
break;
case GTK_MOVEMENT_DISPLAY_LINES:
case GTK_MOVEMENT_PARAGRAPHS:
GtkEditable *editable = GTK_EDITABLE (entry);
gint start_pos = entry->current_pos;
gint end_pos = entry->current_pos;
+ gint old_n_bytes = entry->n_bytes;
_gtk_entry_reset_im_context (entry);
if (!entry->editable)
- return;
+ {
+ gtk_widget_error_bell (GTK_WIDGET (entry));
+ return;
+ }
if (entry->selection_bound != entry->current_pos)
{
gtk_entry_delete_whitespace (entry);
break;
}
-
+
+ if (entry->n_bytes == old_n_bytes)
+ gtk_widget_error_bell (GTK_WIDGET (entry));
+
gtk_entry_pend_cursor_blink (entry);
}
_gtk_entry_reset_im_context (entry);
if (!entry->editable || !entry->text)
- return;
+ {
+ gtk_widget_error_bell (GTK_WIDGET (entry));
+ return;
+ }
if (entry->selection_bound != entry->current_pos)
{
g_free (log_attrs);
}
+ else
+ {
+ gtk_widget_error_bell (GTK_WIDGET (entry));
+ }
gtk_entry_pend_cursor_blink (entry);
}
if (gtk_editable_get_selection_bounds (editable, &start, &end))
gtk_editable_delete_text (editable, start, end);
}
+ else
+ {
+ gtk_widget_error_bell (GTK_WIDGET (entry));
+ }
}
static void
{
if (entry->editable)
gtk_entry_paste (entry, GDK_NONE);
+ else
+ gtk_widget_error_bell (GTK_WIDGET (entry));
}
static void
}
if (!item)
- return;
+ {
+ gtk_widget_error_bell (GTK_WIDGET (icon_view));
+ return;
+ }
if (icon_view->priv->ctrl_pressed ||
!icon_view->priv->shift_pressed ||
icon_view->priv->cursor_item,
count);
+ if (item == icon_view->priv->cursor_item)
+ gtk_widget_error_bell (GTK_WIDGET (icon_view));
+
if (!item)
return;
}
if (!item)
- return;
+ {
+ gtk_widget_error_bell (GTK_WIDGET (icon_view));
+ return;
+ }
if (icon_view->priv->ctrl_pressed ||
!icon_view->priv->shift_pressed ||
item = list ? list->data : NULL;
+ if (item == icon_view->priv->cursor_item)
+ gtk_widget_error_bell (GTK_WIDGET (icon_view));
+
if (!item)
return;
#include <gdk/gdkkeysyms.h>
#include "gtkaccelgroup.h"
#include "gtkimcontextsimple.h"
+#include "gtksettings.h"
+#include "gtkwidget.h"
#include "gtkintl.h"
#include "gtkalias.h"
return TRUE;
}
+static void
+beep_window (GdkWindow *window)
+{
+ GtkWidget *widget;
+
+ gdk_window_get_user_data (window, &widget);
+
+ if (GTK_IS_WIDGET (widget))
+ {
+ gtk_widget_error_bell (widget);
+ }
+ else
+ {
+ GdkScreen *screen = gdk_drawable_get_screen (GDK_DRAWABLE (window));
+ gboolean beep;
+
+ g_object_get (gtk_settings_get_for_screen (screen),
+ "gtk-error-bell", &beep,
+ NULL);
+
+ if (beep)
+ gdk_window_beep (window);
+ }
+}
+
static gboolean
no_sequence_matches (GtkIMContextSimple *context_simple,
gint n_compose,
context_simple->compose_buffer[0] = 0;
if (n_compose > 1) /* Invalid sequence */
{
- gdk_display_beep (gdk_drawable_get_display (event->window));
+ beep_window (event->window);
return TRUE;
}
else
{
/* invalid hex sequence */
- gdk_display_beep (gdk_drawable_get_display (event->window));
+ beep_window (event->window);
context_simple->tentative_match = 0;
context_simple->in_hex_sequence = FALSE;
{
/* invalid hex sequence */
if (n_compose > 0)
- gdk_display_beep (gdk_drawable_get_display (event->window));
+ beep_window (event->window);
context_simple->tentative_match = 0;
context_simple->in_hex_sequence = FALSE;
else if (!is_hex_end)
{
/* non-hex character in hex sequence */
- gdk_display_beep (gdk_drawable_get_display (event->window));
+ beep_window (event->window);
return TRUE;
}
else
{
/* invalid hex sequence */
- gdk_display_beep (gdk_drawable_get_display (event->window));
+ beep_window (event->window);
context_simple->tentative_match = 0;
context_simple->in_hex_sequence = FALSE;
}
}
else if (!check_hex (context_simple, n_compose))
- gdk_display_beep (gdk_drawable_get_display (event->window));
+ beep_window (event->window);
g_signal_emit_by_name (context_simple, "preedit_changed");
/* barf if there was nothing to activate */
g_warning ("Couldn't find a target for a mnemonic activation.");
- gdk_display_beep (gtk_widget_get_display (widget));
-
+ gtk_widget_error_bell (widget);
+
return FALSE;
}
gint count,
gboolean extend_selection)
{
+ gint old_pos;
gint new_pos;
if (label->select_info == NULL)
return;
-
- new_pos = label->select_info->selection_end;
+
+ old_pos = new_pos = label->select_info->selection_end;
if (label->select_info->selection_end != label->select_info->selection_anchor &&
!extend_selection)
new_pos = end_is_left ? label->select_info->selection_end : label->select_info->selection_anchor;
else
new_pos = !end_is_left ? label->select_info->selection_end : label->select_info->selection_anchor;
-
break;
}
case GTK_MOVEMENT_LOGICAL_POSITIONS:
break;
case GTK_MOVEMENT_VISUAL_POSITIONS:
new_pos = gtk_label_move_visually (label, new_pos, count);
+ if (new_pos == old_pos)
+ {
+ if (!extend_selection)
+ {
+ if (!gtk_widget_keynav_failed (GTK_WIDGET (label),
+ count > 0 ?
+ GTK_DIR_RIGHT : GTK_DIR_LEFT))
+ {
+ GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (label));
+
+ if (toplevel)
+ gtk_widget_child_focus (toplevel,
+ count > 0 ?
+ GTK_DIR_RIGHT : GTK_DIR_LEFT);
+ }
+ }
+ else
+ {
+ gtk_widget_error_bell (GTK_WIDGET (label));
+ }
+ }
break;
case GTK_MOVEMENT_WORDS:
while (count > 0)
new_pos = gtk_label_move_backward_word (label, new_pos);
count++;
}
+ if (new_pos == old_pos)
+ gtk_widget_error_bell (GTK_WIDGET (label));
break;
case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
case GTK_MOVEMENT_PARAGRAPH_ENDS:
case GTK_MOVEMENT_BUFFER_ENDS:
/* FIXME: Can do better here */
new_pos = count < 0 ? 0 : strlen (label->text);
+ if (new_pos == old_pos)
+ gtk_widget_error_bell (GTK_WIDGET (label));
break;
case GTK_MOVEMENT_DISPLAY_LINES:
case GTK_MOVEMENT_PARAGRAPHS:
* (basically, those items are accelerator-locked).
*/
/* g_print("item has no path or is locked, menu prefix: %s\n", menu->accel_path); */
- gdk_display_beep (display);
+ gtk_widget_error_bell (widget);
}
else
{
* locked already
*/
/* g_print("failed to change\n"); */
- gdk_display_beep (display);
+ gtk_widget_error_bell (widget);
}
}
}
GList *node = g_list_find (menu_shell->children,
menu_shell->active_menu_item);
GList *start_node = node;
-
+ gboolean wrap_around;
+
+ g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_shell)),
+ "gtk-keynav-wrap-around", &wrap_around,
+ NULL);
+
if (distance > 0)
{
node = node->next;
while (node != start_node &&
(!node || !_gtk_menu_item_is_selectable (node->data)))
{
- if (!node)
- node = menu_shell->children;
- else
+ if (node)
node = node->next;
+ else if (wrap_around)
+ node = menu_shell->children;
+ else
+ {
+ gtk_widget_error_bell (GTK_WIDGET (menu_shell));
+ break;
+ }
}
}
else
while (node != start_node &&
(!node || !_gtk_menu_item_is_selectable (node->data)))
{
- if (!node)
- node = g_list_last (menu_shell->children);
- else
+ if (node)
node = node->prev;
+ else if (wrap_around)
+ node = g_list_last (menu_shell->children);
+ else
+ {
+ gtk_widget_error_bell (GTK_WIDGET (menu_shell));
+ break;
+ }
}
}
while (offset != 0)
{
- current = gtk_notebook_search_page (notebook, current, offset < 0 ? STEP_PREV : STEP_NEXT, TRUE);
+ current = gtk_notebook_search_page (notebook, current,
+ offset < 0 ? STEP_PREV : STEP_NEXT,
+ TRUE);
+
+ if (!current)
+ {
+ gboolean wrap_around;
+
+ g_object_get (gtk_widget_get_settings (GTK_WIDGET (notebook)),
+ "gtk-keynav-wrap-around", &wrap_around,
+ NULL);
+
+ if (wrap_around)
+ current = gtk_notebook_search_page (notebook, NULL,
+ offset < 0 ? STEP_PREV : STEP_NEXT,
+ TRUE);
+ else
+ break;
+ }
+
offset += offset < 0 ? 1 : -1;
}
if (current)
gtk_notebook_switch_page (notebook, current->data, -1);
else
- gdk_display_beep (gtk_widget_get_display (GTK_WIDGET (notebook)));
+ gtk_widget_error_bell (GTK_WIDGET (notebook));
}
static GtkDirectionType
new_page = gtk_notebook_search_page (notebook, notebook->focus_tab,
search_direction, TRUE);
+ if (!new_page)
+ {
+ gboolean wrap_around;
+
+ g_object_get (gtk_widget_get_settings (GTK_WIDGET (notebook)),
+ "gtk-keynav-wrap-around", &wrap_around,
+ NULL);
+
+ if (wrap_around)
+ new_page = gtk_notebook_search_page (notebook, NULL,
+ search_direction, TRUE);
+ }
+
if (new_page)
gtk_notebook_switch_focus_tab (notebook, new_page);
else
- gdk_display_beep (gtk_widget_get_display (GTK_WIDGET (notebook)));
-
+ gtk_widget_error_bell (GTK_WIDGET (notebook));
+
return TRUE;
}
if (!new_focus)
{
+ if (!gtk_widget_keynav_failed (widget, direction))
+ {
+ g_slist_free (focus_list);
+ return FALSE;
+ }
+
tmp_list = focus_list;
while (tmp_list)
if (new_focus)
{
+ GtkSettings *settings = gtk_widget_get_settings (widget);
+ gboolean cursor_only;
+
+ g_object_get (settings,
+ "gtk-keynav-cursor-only", &cursor_only,
+ NULL);
+
gtk_widget_grab_focus (new_focus);
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (new_focus), TRUE);
+
+ if (!cursor_only)
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (new_focus), TRUE);
}
return TRUE;
gtk_range_move_slider (GtkRange *range,
GtkScrollType scroll)
{
+ gboolean cursor_only;
+
+ g_object_get (gtk_widget_get_settings (GTK_WIDGET (range)),
+ "gtk-keynav-cursor-only", &cursor_only,
+ NULL);
+
+ if (cursor_only)
+ {
+ GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (range));
+
+ if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ if (scroll == GTK_SCROLL_STEP_UP ||
+ scroll == GTK_SCROLL_STEP_DOWN)
+ {
+ if (toplevel)
+ gtk_widget_child_focus (toplevel,
+ scroll == GTK_SCROLL_STEP_UP ?
+ GTK_DIR_UP : GTK_DIR_DOWN);
+ return;
+ }
+ }
+ else
+ {
+ if (scroll == GTK_SCROLL_STEP_LEFT ||
+ scroll == GTK_SCROLL_STEP_RIGHT)
+ {
+ if (toplevel)
+ gtk_widget_child_focus (toplevel,
+ scroll == GTK_SCROLL_STEP_LEFT ?
+ GTK_DIR_LEFT : GTK_DIR_RIGHT);
+ return;
+ }
+ }
+ }
+
gtk_range_scroll (range, scroll);
/* Policy DELAYED makes sense with key events,
PROP_COLOR_SCHEME,
PROP_ENABLE_ANIMATIONS,
PROP_TOUCHSCREEN_MODE,
+ PROP_KEYNAV_CURSOR_ONLY,
+ PROP_KEYNAV_WRAP_AROUND,
+ PROP_ERROR_BELL,
PROP_COLOR_HASH
};
/**
* GtkSettings:gtk-touchscreen-mode:
*
- * When TRUE, there are no motion notify events delivered on this screen,
+ * When %TRUE, there are no motion notify events delivered on this screen,
* and widgets can't use the pointer hovering them for any essential
* functionality.
*
g_assert (result == PROP_TOUCHSCREEN_MODE);
+ /**
+ * GtkSettings:gtk-keynav-cursor-only:
+ *
+ * When %TRUE, keyboard navigation should be able to reach all widgets
+ * by using the cursor keys only. Tab, Shift etc. keys can't be expected
+ * to be present on the used input device.
+ *
+ * Since: 2.12
+ */
+ result = settings_install_property_parser (class,
+ g_param_spec_boolean ("gtk-keynav-cursor-only",
+ P_("Keynav Cursor Only"),
+ P_("When TRUE, there are only cursor keys available to navigate widgets"),
+ FALSE,
+ GTK_PARAM_READWRITE),
+ NULL);
+
+ g_assert (result == PROP_KEYNAV_CURSOR_ONLY);
+
+ /**
+ * GtkSettings:gtk-keynav-wrap-around:
+ *
+ * When %TRUE, some widgets will wrap around when doing keyboard
+ * navigation, such as menus, menubars and notebooks.
+ *
+ * Since: 2.12
+ */
+ result = settings_install_property_parser (class,
+ g_param_spec_boolean ("gtk-keynav-wrap-around",
+ P_("Keynav Wrap Around"),
+ P_("Whether to wrap around when keyboard-navigating widgets"),
+ TRUE,
+ GTK_PARAM_READWRITE),
+ NULL);
+
+ g_assert (result == PROP_KEYNAV_WRAP_AROUND);
+
+ /**
+ * GtkSettings:gtk-error-bell:
+ *
+ * When %TRUE, keyboard navigation and other input-related errors
+ * will cause a beep. Since the error bell is implemented using
+ * gdk_window_beep(), the windowing system may offer ways to
+ * configure the error bell in many ways, such as flashing the
+ * window or similar visual effects.
+ *
+ * Since: 2.12
+ */
+ result = settings_install_property_parser (class,
+ g_param_spec_boolean ("gtk-error-bell",
+ P_("Error Bell"),
+ P_("When TRUE, keyboard navigation and other errors will cause a beep"),
+ TRUE,
+ GTK_PARAM_READWRITE),
+ NULL);
+
+ g_assert (result == PROP_ERROR_BELL);
+
/**
* GtkSettings:color-hash:
*
gtk_spin_button_real_change_value (GtkSpinButton *spin,
GtkScrollType scroll)
{
+ gdouble old_value = spin->adjustment->value;
+
/* We don't test whether the entry is editable, since
* this key binding conceptually corresponds to changing
* the value with the buttons using the mouse, which
}
gtk_spin_button_update (spin);
+
+ if (spin->adjustment->value == old_value)
+ gtk_widget_error_bell (GTK_WIDGET (spin));
}
static gint
GtkScrollStep step,
gint count);
static void gtk_text_view_set_anchor (GtkTextView *text_view);
-static void gtk_text_view_scroll_pages (GtkTextView *text_view,
+static gboolean gtk_text_view_scroll_pages (GtkTextView *text_view,
gint count,
gboolean extend_selection);
-static void gtk_text_view_scroll_hpages (GtkTextView *text_view,
+static gboolean gtk_text_view_scroll_hpages(GtkTextView *text_view,
gint count,
gboolean extend_selection);
static void gtk_text_view_insert_at_cursor (GtkTextView *text_view,
gtk_text_view_reset_blink_time (text_view);
gtk_text_view_pend_cursor_blink (text_view);
+ if (!retval && event->length)
+ gtk_widget_error_bell (widget);
+
return retval;
}
{
GtkTextIter insert;
GtkTextIter newplace;
-
gint cursor_x_pos = 0;
+ GtkDirectionType leave_direction = -1;
if (!text_view->cursor_visible)
{
if (step == GTK_MOVEMENT_PAGES)
{
- gtk_text_view_scroll_pages (text_view, count, extend_selection);
+ if (!gtk_text_view_scroll_pages (text_view, count, extend_selection))
+ gtk_widget_error_bell (GTK_WIDGET (text_view));
+
gtk_text_view_check_cursor_blink (text_view);
gtk_text_view_pend_cursor_blink (text_view);
return;
}
else if (step == GTK_MOVEMENT_HORIZONTAL_PAGES)
{
- gtk_text_view_scroll_hpages (text_view, count, extend_selection);
+ if (!gtk_text_view_scroll_hpages (text_view, count, extend_selection))
+ gtk_widget_error_bell (GTK_WIDGET (text_view));
+
gtk_text_view_check_cursor_blink (text_view);
gtk_text_view_pend_cursor_blink (text_view);
return;
case GTK_MOVEMENT_DISPLAY_LINES:
if (count < 0)
- {
- if (gtk_text_view_move_iter_by_lines (text_view, &newplace, count))
- gtk_text_layout_move_iter_to_x (text_view->layout, &newplace, cursor_x_pos);
- else
- gtk_text_iter_set_line_offset (&newplace, 0);
- }
+ {
+ leave_direction = GTK_DIR_UP;
+
+ if (gtk_text_view_move_iter_by_lines (text_view, &newplace, count))
+ gtk_text_layout_move_iter_to_x (text_view->layout, &newplace, cursor_x_pos);
+ else
+ gtk_text_iter_set_line_offset (&newplace, 0);
+ }
if (count > 0)
- {
- if (gtk_text_view_move_iter_by_lines (text_view, &newplace, count))
- gtk_text_layout_move_iter_to_x (text_view->layout, &newplace, cursor_x_pos);
- else
- gtk_text_iter_forward_to_line_end (&newplace);
- }
+ {
+ leave_direction = GTK_DIR_DOWN;
+
+ if (gtk_text_view_move_iter_by_lines (text_view, &newplace, count))
+ gtk_text_layout_move_iter_to_x (text_view->layout, &newplace, cursor_x_pos);
+ else
+ gtk_text_iter_forward_to_line_end (&newplace);
+ }
break;
case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
if (step == GTK_MOVEMENT_DISPLAY_LINES)
gtk_text_view_set_virtual_cursor_pos (text_view, cursor_x_pos, -1);
}
+ else if (leave_direction != -1)
+ {
+ if (!gtk_widget_keynav_failed (GTK_WIDGET (text_view),
+ leave_direction))
+ {
+ gtk_text_view_move_focus (text_view, leave_direction);
+ }
+ }
+ else
+ {
+ gtk_widget_error_bell (GTK_WIDGET (text_view));
+ }
gtk_text_view_check_cursor_blink (text_view);
gtk_text_view_pend_cursor_blink (text_view);
gtk_text_buffer_create_mark (get_buffer (text_view), "anchor", &insert, TRUE);
}
-static void
+static gboolean
gtk_text_view_scroll_pages (GtkTextView *text_view,
gint count,
gboolean extend_selection)
gdouble oldval;
GtkAdjustment *adj;
gint cursor_x_pos, cursor_y_pos;
+ GtkTextIter old_insert;
GtkTextIter new_insert;
GtkTextIter anchor;
gint y0, y1;
- g_return_if_fail (text_view->vadjustment != NULL);
+ g_return_val_if_fail (text_view->vadjustment != NULL, FALSE);
adj = text_view->vadjustment;
gtk_text_view_scroll_mark_onscreen (text_view,
gtk_text_buffer_get_mark (get_buffer (text_view),
"insert"));
-
-/* Validate the region that will be brought into view by the cursor motion
+
+ /* Validate the region that will be brought into view by the cursor motion
*/
+ gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
+ &old_insert,
+ gtk_text_buffer_get_mark (get_buffer (text_view), "insert"));
+
if (count < 0)
{
gtk_text_view_get_first_para_iter (text_view, &anchor);
gtk_text_layout_validate_yrange (text_view->layout, &anchor, y0, y1);
/* FIXME do we need to update the adjustment ranges here? */
+ new_insert = old_insert;
+
if (count < 0 && adj->value <= (adj->lower + 1e-12))
{
/* already at top, just be sure we are at offset 0 */
{
gtk_text_view_get_virtual_cursor_pos (text_view, &cursor_x_pos, &cursor_y_pos);
- newval = adj->value;
oldval = adj->value;
-
+ newval = adj->value;
+
newval += count * adj->page_increment;
set_adjustment_clamped (adj, newval);
gtk_text_view_scroll_mark_onscreen (text_view,
gtk_text_buffer_get_mark (get_buffer (text_view),
"insert"));
+
+ return !gtk_text_iter_equal (&old_insert, &new_insert);
}
-static void
+static gboolean
gtk_text_view_scroll_hpages (GtkTextView *text_view,
gint count,
gboolean extend_selection)
gdouble oldval;
GtkAdjustment *adj;
gint cursor_x_pos, cursor_y_pos;
+ GtkTextIter old_insert;
GtkTextIter new_insert;
gint y, height;
- g_return_if_fail (text_view->hadjustment != NULL);
+ g_return_val_if_fail (text_view->hadjustment != NULL, FALSE);
adj = text_view->hadjustment;
gtk_text_view_scroll_mark_onscreen (text_view,
gtk_text_buffer_get_mark (get_buffer (text_view),
"insert"));
-
+
/* Validate the line that we're moving within.
*/
gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
- &new_insert,
+ &old_insert,
gtk_text_buffer_get_mark (get_buffer (text_view), "insert"));
gtk_text_layout_get_line_yrange (text_view->layout, &new_insert, &y, &height);
gtk_text_layout_validate_yrange (text_view->layout, &new_insert, y, y + height);
/* FIXME do we need to update the adjustment ranges here? */
-
+
+ new_insert = old_insert;
+
if (count < 0 && adj->value <= (adj->lower + 1e-12))
{
/* already at far left, just be sure we are at offset 0 */
{
gtk_text_view_get_virtual_cursor_pos (text_view, &cursor_x_pos, &cursor_y_pos);
- newval = adj->value;
oldval = adj->value;
-
+ newval = adj->value;
+
newval += count * adj->page_increment;
set_adjustment_clamped (adj, newval);
gtk_text_view_scroll_mark_onscreen (text_view,
gtk_text_buffer_get_mark (get_buffer (text_view),
"insert"));
+
+ return !gtk_text_iter_equal (&old_insert, &new_insert);
}
static gboolean
gtk_text_view_insert_at_cursor (GtkTextView *text_view,
const gchar *str)
{
- gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), str, -1,
- text_view->editable);
+ if (!gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), str, -1,
+ text_view->editable))
+ {
+ gtk_widget_error_bell (GTK_WIDGET (text_view));
+ }
}
static void
" ", 1,
text_view->editable);
}
+ else
+ {
+ gtk_widget_error_bell (GTK_WIDGET (text_view));
+ }
gtk_text_buffer_end_user_action (get_buffer (text_view));
gtk_text_view_set_virtual_cursor_pos (text_view, -1, -1);
gtk_text_view_scroll_mark_onscreen (text_view,
gtk_text_buffer_get_mark (get_buffer (text_view), "insert"));
}
+ else
+ {
+ gtk_widget_error_bell (GTK_WIDGET (text_view));
+ }
}
static void
gtk_text_view_scroll_mark_onscreen (text_view,
gtk_text_buffer_get_insert (get_buffer (text_view)));
}
+ else
+ {
+ gtk_widget_error_bell (GTK_WIDGET (text_view));
+ }
}
static void
if (str)
{
- gtk_text_buffer_insert_interactive (get_buffer (text_view),
- drop_point, (gchar *) str, -1,
- text_view->editable);
+ if (!gtk_text_buffer_insert_interactive (get_buffer (text_view),
+ drop_point, (gchar *) str, -1,
+ text_view->editable))
+ {
+ gtk_widget_error_bell (GTK_WIDGET (text_view));
+ }
+
g_free (str);
}
}
if (!strcmp (str, "\n"))
{
- gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), "\n", 1,
- text_view->editable);
+ if (!gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), "\n", 1,
+ text_view->editable))
+ {
+ gtk_widget_error_bell (GTK_WIDGET (text_view));
+ }
}
else
{
if (!gtk_text_iter_ends_line (&insert))
gtk_text_view_delete_from_cursor (text_view, GTK_DELETE_CHARS, 1);
}
- gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), str, -1,
- text_view->editable);
+
+ if (!gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), str, -1,
+ text_view->editable))
+ {
+ gtk_widget_error_bell (GTK_WIDGET (text_view));
+ }
}
gtk_text_buffer_end_user_action (get_buffer (text_view));
static gboolean gtk_tree_view_search_key_press_event (GtkWidget *entry,
GdkEventKey *event,
GtkTreeView *tree_view);
-static void gtk_tree_view_search_move (GtkWidget *window,
+static gboolean gtk_tree_view_search_move (GtkWidget *window,
GtkTreeView *tree_view,
gboolean up);
static gboolean gtk_tree_view_search_equal_func (GtkTreeModel *model,
GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (focus_column->data);
if (!column->resizable)
- return TRUE;
+ {
+ gtk_widget_error_bell (widget);
+ return TRUE;
+ }
if (event->keyval == (rtl ? GDK_Right : GDK_Left)
|| event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
{
+ gint old_width = column->resized_width;
+
column->resized_width = MAX (column->resized_width,
column->width);
column->resized_width -= 2;
column->max_width);
column->use_resized_width = TRUE;
- gtk_widget_queue_resize (widget);
+
+ if (column->resized_width != old_width)
+ gtk_widget_queue_resize (widget);
+ else
+ gtk_widget_error_bell (widget);
}
else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
|| event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
{
+ gint old_width = column->resized_width;
+
column->resized_width = MAX (column->resized_width,
column->width);
column->resized_width += 2;
column->max_width);
column->use_resized_width = TRUE;
- gtk_widget_queue_resize (widget);
+
+ if (column->resized_width != old_width)
+ gtk_widget_queue_resize (widget);
+ else
+ gtk_widget_error_bell (widget);
}
return TRUE;
col = gtk_tree_view_get_drop_column (tree_view, column, DROP_LEFT);
if (col != (GtkTreeViewColumn *)0x1)
gtk_tree_view_move_column_after (tree_view, column, col);
+ else
+ gtk_widget_error_bell (widget);
}
else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
|| event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
col = gtk_tree_view_get_drop_column (tree_view, column, DROP_RIGHT);
if (col != (GtkTreeViewColumn *)0x1)
gtk_tree_view_move_column_after (tree_view, column, col);
+ else
+ gtk_widget_error_bell (widget);
}
else if (event->keyval == GDK_Home || event->keyval == GDK_KP_Home)
{
col = gtk_tree_view_get_drop_column (tree_view, column, DROP_HOME);
if (col != (GtkTreeViewColumn *)0x1)
gtk_tree_view_move_column_after (tree_view, column, col);
+ else
+ gtk_widget_error_bell (widget);
}
else if (event->keyval == GDK_End || event->keyval == GDK_KP_End)
{
col = gtk_tree_view_get_drop_column (tree_view, column, DROP_END);
if (col != (GtkTreeViewColumn *)0x1)
gtk_tree_view_move_column_after (tree_view, column, col);
+ else
+ gtk_widget_error_bell (widget);
}
return TRUE;
|| event->keyval == GDK_Right || event->keyval == GDK_KP_Right))
{
if ((event->keyval == (rtl ? GDK_Right : GDK_Left)
- || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
- && focus_column->prev)
+ || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left)))
{
GList *tmp;
break;
if (!tmp)
- return FALSE;
+ {
+ gtk_widget_error_bell (widget);
+ return TRUE;
+ }
tree_view->priv->focus_column = GTK_TREE_VIEW_COLUMN (tmp->data);
gtk_widget_grab_focus (tree_view->priv->focus_column->button);
tree_view->priv->hadjustment->upper - tree_view->priv->hadjustment->page_size));
}
else if ((event->keyval == (rtl ? GDK_Left : GDK_Right)
- || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
- && focus_column->next)
+ || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right)))
{
GList *tmp;
break;
if (!tmp)
- return FALSE;
+ {
+ gtk_widget_error_bell (widget);
+ return TRUE;
+ }
tree_view->priv->focus_column = GTK_TREE_VIEW_COLUMN (tmp->data);
else
{
gtk_tree_view_clamp_node_visible (tree_view, cursor_tree, cursor_node);
+ gtk_widget_error_bell (GTK_WIDGET (tree_view));
}
gtk_widget_grab_focus (GTK_WIDGET (tree_view));
{
GtkRBTree *cursor_tree = NULL;
GtkRBNode *cursor_node = NULL;
+ GtkTreePath *old_cursor_path = NULL;
GtkTreePath *cursor_path = NULL;
gint y;
gint window_y;
return;
if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
- cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
+ old_cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
else
/* This is sorta weird. Focus in should give us a cursor */
return;
gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical-separator", &vertical_separator, NULL);
- _gtk_tree_view_find_node (tree_view, cursor_path,
+ _gtk_tree_view_find_node (tree_view, old_cursor_path,
&cursor_tree, &cursor_node);
- gtk_tree_path_free (cursor_path);
-
if (cursor_tree == NULL)
- /* FIXME: we lost the cursor. Should we try to get one? */
- return;
+ {
+ /* FIXME: we lost the cursor. Should we try to get one? */
+ gtk_tree_path_free (old_cursor_path);
+ return;
+ }
g_return_if_fail (cursor_node != NULL);
y = _gtk_rbtree_node_find_offset (cursor_tree, cursor_node);
cursor_path = _gtk_tree_view_find_path (tree_view, cursor_tree, cursor_node);
g_return_if_fail (cursor_path != NULL);
gtk_tree_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE);
- gtk_tree_path_free (cursor_path);
y -= window_y;
gtk_tree_view_scroll_to_point (tree_view, -1, y);
_gtk_tree_view_queue_draw_node (tree_view, cursor_tree, cursor_node, NULL);
+
+ if (!gtk_tree_path_compare (old_cursor_path, cursor_path))
+ gtk_widget_error_bell (GTK_WIDGET (tree_view));
+
+ gtk_tree_path_free (old_cursor_path);
+ gtk_tree_path_free (cursor_path);
}
static void
NULL);
g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
}
+ else
+ {
+ gtk_widget_error_bell (GTK_WIDGET (tree_view));
+ }
+
gtk_tree_view_clamp_column_visible (tree_view, tree_view->priv->focus_column);
}
GtkRBTree *cursor_tree;
GtkRBNode *cursor_node;
GtkTreePath *path;
+ GtkTreePath *old_path;
if (! GTK_WIDGET_HAS_FOCUS (tree_view))
return;
g_return_if_fail (tree_view->priv->tree != NULL);
+ gtk_tree_view_get_cursor (tree_view, &old_path, NULL);
+
+ cursor_tree = tree_view->priv->tree;
+ cursor_node = cursor_tree->root;
+
if (count == -1)
{
- cursor_tree = tree_view->priv->tree;
- cursor_node = cursor_tree->root;
while (cursor_node && cursor_node->left != cursor_tree->nil)
cursor_node = cursor_node->left;
}
else
{
- cursor_tree = tree_view->priv->tree;
- cursor_node = cursor_tree->root;
do
{
while (cursor_node && cursor_node->right != cursor_tree->nil)
}
path = _gtk_tree_view_find_path (tree_view, cursor_tree, cursor_node);
- gtk_tree_view_real_set_cursor (tree_view, path, TRUE, TRUE);
+
+ if (gtk_tree_path_compare (old_path, path))
+ {
+ gtk_tree_view_real_set_cursor (tree_view, path, TRUE, TRUE);
+ }
+ else
+ {
+ gtk_widget_error_bell (GTK_WIDGET (tree_view));
+ }
+
+ gtk_tree_path_free (old_path);
gtk_tree_path_free (path);
}
/* select previous matching iter */
if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
{
- gtk_tree_view_search_move (widget, tree_view, TRUE);
+ if (!gtk_tree_view_search_move (widget, tree_view, TRUE))
+ gtk_widget_error_bell (widget);
+
retval = TRUE;
}
if (((event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == (GDK_CONTROL_MASK | GDK_SHIFT_MASK))
&& (event->keyval == GDK_g || event->keyval == GDK_G))
{
- gtk_tree_view_search_move (widget, tree_view, TRUE);
+ if (!gtk_tree_view_search_move (widget, tree_view, TRUE))
+ gtk_widget_error_bell (widget);
+
retval = TRUE;
}
/* select next matching iter */
if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
{
- gtk_tree_view_search_move (widget, tree_view, FALSE);
+ if (!gtk_tree_view_search_move (widget, tree_view, FALSE))
+ gtk_widget_error_bell (widget);
+
retval = TRUE;
}
if (((event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == GDK_CONTROL_MASK)
&& (event->keyval == GDK_g || event->keyval == GDK_G))
{
- gtk_tree_view_search_move (widget, tree_view, FALSE);
+ if (!gtk_tree_view_search_move (widget, tree_view, FALSE))
+ gtk_widget_error_bell (widget);
+
retval = TRUE;
}
return retval;
}
-static void
+/* this function returns FALSE if there is a search string but
+ * nothing was found, and TRUE otherwise.
+ */
+static gboolean
gtk_tree_view_search_move (GtkWidget *window,
GtkTreeView *tree_view,
gboolean up)
text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
- g_return_if_fail (text != NULL);
+ g_return_val_if_fail (text != NULL, FALSE);
+
+ len = strlen (text);
if (up && tree_view->priv->selected_iter == 1)
- return;
+ return strlen (text) < 1;
len = strlen (text);
if (len < 1)
- return;
+ return TRUE;
model = gtk_tree_view_get_model (tree_view);
selection = gtk_tree_view_get_selection (tree_view);
/* search */
gtk_tree_selection_unselect_all (selection);
if (!gtk_tree_model_get_iter_first (model, &iter))
- return;
+ return TRUE;
ret = gtk_tree_view_search_iter (model, selection, &iter, text,
&count, up?((tree_view->priv->selected_iter) - 1):((tree_view->priv->selected_iter + 1)));
{
/* found */
tree_view->priv->selected_iter += up?(-1):(1);
+ return TRUE;
}
else
{
gtk_tree_view_search_iter (model, selection,
&iter, text,
&count, tree_view->priv->selected_iter);
+ return FALSE;
}
}
CAN_ACTIVATE_ACCEL,
GRAB_BROKEN,
COMPOSITED_CHANGED,
+ KEYNAV_FAILED,
LAST_SIGNAL
};
GdkEventFocus *event);
static gboolean gtk_widget_real_focus (GtkWidget *widget,
GtkDirectionType direction);
+static gboolean gtk_widget_real_keynav_failed (GtkWidget *widget,
+ GtkDirectionType direction);
static PangoContext* gtk_widget_peek_pango_context (GtkWidget *widget);
static void gtk_widget_update_pango_context (GtkWidget *widget);
static void gtk_widget_propagate_state (GtkWidget *widget,
_gtk_marshal_VOID__VOID,
G_TYPE_NONE, 0);
+ /**
+ * GtkWidget::keynav-failed:
+ * @widget: the object which received the signal.
+ * @direction: the direction of movement
+ *
+ * See gtk_widget_keynav_failed() for details.
+ *
+ * Returns: %TRUE if stopping keyboard navigation is fine, %FALSE
+ * if the emitting widget should try to handle the keyboard
+ * navigation attempt in its parent container(s).
+ *
+ * Since: 2.12
+ **/
+ widget_signals[KEYNAV_FAILED] =
+ _gtk_binding_signal_new (I_("keynav-failed"),
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_CALLBACK (gtk_widget_real_keynav_failed),
+ _gtk_boolean_handled_accumulator, NULL,
+ _gtk_marshal_BOOLEAN__ENUM,
+ G_TYPE_BOOLEAN, 1,
+ GTK_TYPE_DIRECTION_TYPE);
+
/**
* GtkWidget::delete-event:
* @widget: the object which received the signal.
{
g_warning ("widget `%s' isn't suitable for mnemonic activation",
G_OBJECT_TYPE_NAME (widget));
- gdk_display_beep (gtk_widget_get_display (widget));
+ gtk_widget_error_bell (widget);
}
return TRUE;
}
return FALSE;
}
+static gboolean
+gtk_widget_real_keynav_failed (GtkWidget *widget,
+ GtkDirectionType direction)
+{
+ gboolean cursor_only;
+
+ switch (direction)
+ {
+ case GTK_DIR_TAB_FORWARD:
+ case GTK_DIR_TAB_BACKWARD:
+ return FALSE;
+
+ case GTK_DIR_UP:
+ case GTK_DIR_DOWN:
+ case GTK_DIR_LEFT:
+ case GTK_DIR_RIGHT:
+ g_object_get (gtk_widget_get_settings (widget),
+ "gtk-keynav-cursor-only", &cursor_only,
+ NULL);
+ if (cursor_only)
+ return FALSE;
+ break;
+ }
+
+ gtk_widget_error_bell (widget);
+
+ return TRUE;
+}
+
/**
* gtk_widget_is_focus:
* @widget: a #GtkWidget
return return_val;
}
+/**
+ * gtk_widget_keynav_failed:
+ * @widget: a #GtkWidget
+ * @direction: direction of focus movement
+ *
+ * This function should be called whenever keyboard navigation within
+ * a single widget hits a boundary. The function emits the
+ * "keynav-changed" signal on the widget and its return value should
+ * be interpreted in a way similar to the return value of
+ * gtk_widget_child_focus():
+ *
+ * When %TRUE is returned, stay in the widget, the failed keyboard
+ * navigation is Ok and/or there is nowhere we can/should move the
+ * focus to.
+ *
+ * When %FALSE is returned, the caller should continue with keyboard
+ * navigation outside the widget, e.g. by calling
+ * gtk_widget_child_focus() on the widget's toplevel.
+ *
+ * The default implementation for the "keynav-failed" signal is to
+ * return %TRUE for %GTK_DIR_TAB_FORWARD and
+ * %GTK_DIR_TAB_BACKWARD. For the other values of #GtkDirectionType,
+ * it looks at the "gtk-keynav-cursor-only" settings property and
+ * returns %FALSE if the setting is %TRUE. This way the entire GUI
+ * becomes cursor-navigatable on input devices such as mobile phones
+ * which only have cursor keys but no tab key.
+ *
+ * Whenever the default implementation returns %TRUE, it also calls
+ * gtk_widget_error_bell() to notify the user of the failed keyboard
+ * navigation.
+ *
+ * A use case for providing an own implementation of keynav-failed (by
+ * either connecting to it or by overriding it) would be a row of
+ * #GtkEntry widgets where the user should be able to navigate the
+ * entire row with the cursor keys, as e.g. known from GUIs that
+ * require entering license keys.
+ *
+ * Return value: %TRUE if stopping keyboard navigation is fine, %FALSE
+ * if the emitting widget should try to handle the keyboard
+ * navigation attempt in its parent container(s).
+ *
+ * Since: 2.12
+ **/
+gboolean
+gtk_widget_keynav_failed (GtkWidget *widget,
+ GtkDirectionType direction)
+{
+ gboolean return_val;
+
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
+
+ g_signal_emit (widget, widget_signals[KEYNAV_FAILED], 0,
+ direction, &return_val);
+
+ return return_val;
+}
+
+/**
+ * gtk_widget_error_bell:
+ * @widget: a #GtkWidget
+ *
+ * Notifies the user about an input-related error on this widget. If
+ * the gtk-error-bell settings property is %TRUE, it calls
+ * gdk_window_beep(), otherwise it does nothing.
+ *
+ * Note that the effect of gdk_window_beep() can be configured in many
+ * ways, depending on the windowing backend and the desktop environment
+ * or window manager that is used.
+ *
+ * Since: 2.12
+ **/
+void
+gtk_widget_error_bell (GtkWidget *widget)
+{
+ gboolean beep;
+
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ g_object_get (gtk_widget_get_settings (widget),
+ "gtk-error-bell", &beep,
+ NULL);
+
+ if (beep && widget->window)
+ gdk_window_beep (widget->window);
+}
+
/**
* gtk_widget_set_uposition:
* @widget: a #GtkWidget
GdkEventGrabBroken *event);
void (* composited_changed) (GtkWidget *widget);
-
+
/* Padding for future expansion */
void (*_gtk_reserved4) (void);
void (*_gtk_reserved5) (void);
gboolean gtk_widget_child_focus (GtkWidget *widget,
GtkDirectionType direction);
+gboolean gtk_widget_keynav_failed (GtkWidget *widget,
+ GtkDirectionType direction);
+void gtk_widget_error_bell (GtkWidget *widget);
void gtk_widget_set_size_request (GtkWidget *widget,
gint width,